package com.models
{
	import mx.core.UIComponent;
	import com.events.ConnectEvent;
	import com.views.Activity;
	import com.views.Decision;
	import com.data.ConnectionDAO;
	import com.views.Connection;
	import com.views.IGraphElement;
	import com.data.ActivityDAO;
	import com.data.DecisionDAO;
	import com.data.IGraphElementDAO;
	import com.events.EditActivityEvent;
	import com.events.EditDecisionEvent;
	import com.events.GraphEditEvent;
	import mx.managers.CursorManager;
	import com.data.ToolIcon;
	import flash.geom.Point;
	import mx.collections.ArrayCollection;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	import flash.net.URLLoader;
	import flash.events.Event;
	
	/**
	 * Model
	 * 
	 */
	public class WorkFlow extends UIComponent
	{
		private var _dataList:Object;
		private var _viewList:Object;
		private var _connectionOutDataList:Object;
		private var _connectionInDataList:Object;
		private var _connectionViewList:Object;
		private var _selectedElement:IGraphElement;
		private var _tmpSelectedElementDAO:IGraphElementDAO;
		private var _proxy:UIComponent;
		private var _tool:String;
		private var _cursorId:int;
		private var _userList:ArrayCollection;
		private var _startId:String;

		
		public const TYPE_ACTIVITY:String = "activity";
		public const TYPE_DECISION:String = "decision";
		public const TYPE_CONNECTION:String = "connection"; 
		
		/** 
		 * Konstruktor
		 */
		public function WorkFlow()
		{
			_dataList = {};
			_connectionOutDataList = {};
			_connectionInDataList = {};
			_viewList = {};
			_connectionViewList = {};
			_userList = new ArrayCollection();
		}
		
		/**
		 * Hinzufügen einer Aktivität
		 * @param activity
		 * 
		 */
		public function addActivity(pPos:Point = null):void
		{
			var data:Object = new Object();
			var activity:ActivityDAO = new ActivityDAO();
			data["element"] = activity;
			data["position"] = pPos;
			activity.dataProvider = _userList;
			_dataList[activity.dataId] = activity;
			var evt:EditActivityEvent = new EditActivityEvent(EditActivityEvent.CREATE, data);
			dispatchEvent(evt);
		}
		/**
		 * Entfernen einer Aktivität
		 * 
		 */
		public function removeActivity(pDataId:String):void
		{
			if(_dataList[pDataId])
			{
				if(_connectionOutDataList[pDataId])
				{
					
					var arr:Array = _connectionOutDataList[pDataId];
					for(var i:int = 0; i < arr.length; i++)
					{
						
						var data:ConnectionDAO = arr[i];
						var initiator:IGraphElementDAO = (_dataList[data.initiatorDataId] as IGraphElementDAO);
						initiator.postList[data.targetDataId] = null;
						
						
						
						var connection:Connection = getConnection(data.dataId);
						
						var connectEvent:ConnectEvent = new ConnectEvent(ConnectEvent.DELETE, connection);
						dispatchEvent(connectEvent);
						
						_connectionViewList[connection.dataId] = null;
						_dataList[connection.dataId] = null;
					}
					
					_connectionOutDataList[pDataId] = null;
				}
				var prevList:Object = (_dataList[pDataId] as IGraphElementDAO).prevList;
				for each(var item:* in prevList)
				{
					arr = _connectionInDataList[item.dataId];
					for(i = 0; i < arr.length; i++)
					{
						if(arr[i].dataId == pDataId)
						{
							data = arr[i] as ConnectionDAO;
							
							var target:IGraphElementDAO = (_dataList[data.targetDataId] as IGraphElementDAO);
							target.prevList[data.initiatorDataId] = null;
							
							connection = getConnection(data.dataId);
							
							connectEvent = new ConnectEvent(ConnectEvent.DELETE, connection);
							dispatchEvent(connectEvent);
							
							_connectionViewList[connection.dataId] = null;
							_dataList[connection.dataId] = null;
							arr.splice(i, 1);
							break;
						}
					}
				}
				_dataList[pDataId] = null;
				var activity:Activity = _viewList[pDataId];
				_viewList[pDataId] = null;
				
				
				var activityEvent:EditActivityEvent = new EditActivityEvent(EditActivityEvent.DELETE, activity);
				dispatchEvent(activityEvent);
			}
			else
				throw Error("Eine nicht vorhandene Aktivität wird versucht zu entfernen");
		}
		
		/**
		 * Hinzufügen einer Entscheidung
		 * @param decision
		 * 
		 */
		public function addDecision(pPos:Point = null):void
		{
			var data:Object = new Object();
			var decision:DecisionDAO = new DecisionDAO();
			data["element"] = decision;
			data["position"] = pPos;
			_dataList[decision.dataId] = decision;
			var evt:EditDecisionEvent = new EditDecisionEvent(EditDecisionEvent.CREATE, data);
			dispatchEvent(evt);
		}
		/**
		 * Entfernen einer Entscheidung
		 * 
		 */
		public function removeDecision(pDataId:String):void
		{
			if(_dataList[pDataId])
				_dataList[pDataId] = null;
				//TODO dispatch event
			else
				throw Error("Eine nicht vorhandene Entscheidung wird versucht zu entfernen");
		}
		
		/**
		 * Hinzufügen einer Verbindung
		 * @param initiator Ausgangselement der Verbindung
		 * @param target Endelement der Verbindung
		 * 
		 */
		public function addConnection(pInitiator:IGraphElement, pTarget:IGraphElement):void
		{
				// TODO abfangen wenn Verbindung bereits vorhanden
				var initiatorId:String = pInitiator.dataId;
				var targetId:String = pTarget.dataId;
				var connection:ConnectionDAO = new ConnectionDAO(initiatorId, targetId);
				(_dataList[initiatorId] as IGraphElementDAO).addPost(targetId); //füge child hinzu
				(_dataList[targetId] as IGraphElementDAO).addPrev(initiatorId); //füge parent hinzu
				_dataList[connection.dataId] = connection;
				if(!_connectionOutDataList[initiatorId])
					_connectionOutDataList[initiatorId] = [];
				if(!_connectionInDataList[targetId])
					_connectionInDataList[targetId] = [];
				(_connectionOutDataList[initiatorId] as Array).push(connection);
				(_connectionInDataList[targetId] as Array).push(connection);
				var evt:ConnectEvent = new ConnectEvent(ConnectEvent.CREATE, connection);
				dispatchEvent(evt);
		}
		
		
		/**
		 * Enfernen einer Verbindung
		 * 
		 */
		public function removeConnection(pDataId:String):void
		{
			if(_connectionOutDataList[pDataId])
				_connectionOutDataList[pDataId] = null;
				//TODO dispatch event
			else
				throw Error("Eine nicht vorhandene Verbindung wird versucht zu entfernen");
		}
		
		
		/**
		 * Aktualisieren der Verbindungen zu einem Element
		 * @param initiator das zu berücksichtigende Element
		 * 
		 */
		public function updateConnection(initiator:IGraphElement):void
		{
			var evt:ConnectEvent;
			
			if(_connectionOutDataList[initiator.dataId])
			{
				var connections:Array = _connectionOutDataList[initiator.dataId];
				for(var i:int = 0; i < connections.length; i++)
				{
					var obj:Object = {direction: "out", connection: connections[i]};
					evt = new ConnectEvent(ConnectEvent.UPDATE, obj);
					dispatchEvent(evt);
				}
			}
			if(_connectionInDataList[initiator.dataId])
			{
				connections = _connectionInDataList[initiator.dataId];
				for(i = 0; i < connections.length; i++)
				{
					obj = {direction: "in", connection: connections[i]};
					evt = new ConnectEvent(ConnectEvent.UPDATE, obj);
					dispatchEvent(evt);
				}
			}
		}
		
		/**
		 * Aufspüren aller Verbindungen eines Elements
		 * @param obj das zu berücksichtigende Element
		 * @return ein Array mit allen in Verbindung stehenden Elementen
		 * 
		 */
		protected function getDependency(obj:IGraphElement):Array
		{
			var data:IGraphElementDAO = getData(obj.dataId);
			var prevList:Object = data.prevList;
			
			
			var dependency:Array = [];
			for(var i:int = 0; i < _connectionOutDataList.length; i++)
			{
				if((_connectionOutDataList[i].initiator == obj)||(_connectionOutDataList[i].target == obj))
				{
					dependency.push(i);
				}
			}
			return dependency;
		}
		
		/**
		 * Liefert anhand der dataId das GraphElement
		 * @param pUid
		 * @return 
		 * 
		 */
		public function getData(pDataId:String):IGraphElementDAO
		{
			if(_dataList[pDataId])
				return _dataList[pDataId];
			else
				throw Error("Das angeforderte GraphElement ist nicht vorhanden");
		}
		
		public function getView(pDataId:String):IGraphElement
		{
			return _viewList[pDataId];
		}
		public function setView(pElement:IGraphElement):void
		{
			_viewList[pElement.dataId] = pElement;
		}
		
		public function getConnection(pDataId:String):Connection
		{
			return _connectionViewList[pDataId];
		}
		public function setConnection(pConnection:Connection):void
		{
			_connectionViewList[pConnection.dataId] = pConnection;
		}
		public function getConnectionData(pDataId:String):ConnectionDAO
		{
			return _connectionOutDataList[pDataId];
		}
		
		public function set proxy(pProxy:UIComponent):void
		{
			_proxy = pProxy;
		}
		public function get proxy():UIComponent
		{
			return _proxy;
		}
		
		/**
		 * Setzt das ausgewählte Element
		 * @param pElement das zu setztende Element
		 * 
		 */
		public function setSelectedElement(pElement:IGraphElement = null):void
		{
			var data:IGraphElementDAO;
			if(_selectedElement)
			{
				_selectedElement.selected = false;
			}
			if(pElement)
			{
				_selectedElement = pElement;
				_selectedElement.selected = true;
				data = _dataList[pElement.dataId];
			}
			var evt:GraphEditEvent = new GraphEditEvent(GraphEditEvent.SELECT, data);
			dispatchEvent(evt); 
		}
		
		public function getSelectedElement():IGraphElement
		{
			return _selectedElement;
		}
		
		
		/**
		 * Aktualisieren der im Attibut-Editor veränderten Daten
		 * 
		 */
		public function updateData():void
		{
			var data:IGraphElementDAO = getData(_selectedElement.dataId);
			var evt:GraphEditEvent = new GraphEditEvent(GraphEditEvent.UPDATE, data);
			dispatchEvent(evt);
		}
		
		public function set tool(pTool:String):void
		{
			_tool = pTool;
			switch(_tool)
			{
				case "delete":
					setToolIcon(ToolIcon.DELETE_ICON);
					break;
				case "":
					setToolIcon();
					break;
			}
		}
		public function get tool():String
		{
			return _tool;
		}
		
		public function setToolIcon(pTool:Class = null):void
		{
			if(pTool)
			{
				var oldCursor:Number = _cursorId;
				_cursorId = CursorManager.setCursor(pTool,2,-4,-12);
				CursorManager.removeCursor(oldCursor);
			}
			else
				CursorManager.removeCursor(_cursorId);
		}
		
		public function dataType(pDataId:String):String
		{
			var result:String;
			var str:String = pDataId.substring(0,1);
			switch(str)
			{
				case "a":
					result = TYPE_ACTIVITY;
					break;
				case "d":
					result = TYPE_DECISION;
					break;
				case "c":
					result = TYPE_CONNECTION;
					break;
			}
			return result;
		}
		
		public function newUser(pUser:Object):void
		{
			//TODO: abfangen wenn user schon vorhanden ist
			_userList.addItem(pUser);
			//TODO: event dispatchen um Aktivitäten zu aktualisieren
		}
		public function removeUser(pIndex:int):void
		{
			_userList.removeItemAt(pIndex);
			//TODO: event dispatchen um Aktivitäten zu aktualisieren
		}
		
		public function get userList():ArrayCollection
		{
			return _userList;
		}
		public function set userList(value:ArrayCollection):void
		{
			_userList = value;
		}
		
		public function saveData():void
		{
			var xmlData:XML = <workflow />;
			var activityCount:int = 0;
			var decisionCount:int = 0;
			var connectionCount:int = 0;
			for each(var data:Object in _dataList)
			{
				var dataDAO:IGraphElementDAO = data as IGraphElementDAO;
				switch(dataType(dataDAO.dataId))
				{
					case TYPE_ACTIVITY:
						xmlData.activities.activity[activityCount] = "";
						xmlData.activities.activity[activityCount].@id = dataDAO.dataId;
						xmlData.activities.activity[activityCount].@creator = dataDAO.creator;
						xmlData.activities.activity[activityCount].@label = dataDAO.text;
						xmlData.activities.activity[activityCount].message = dataDAO.message;
						var j:int = 0;
						for each(var access:Object in dataDAO.noAccessList)
						{
							trace(access["name"]);
							xmlData.activities.activity[activityCount]["no-access"][j] = "";
							xmlData.activities.activity[activityCount]["no-access"][j].@name = access["name"];
							xmlData.activities.activity[activityCount]["no-access"][j].@email = access["email"];
							xmlData.activities.activity[activityCount]["no-access"][j].@pass = access["pass"];
							j++;
						}
						activityCount++;
						break;
					case TYPE_CONNECTION:
						xmlData.connections.connection[connectionCount] = dataDAO.message;
						xmlData.connections.connection[connectionCount].@id = dataDAO.dataId;
						xmlData.connections.connection[connectionCount].@start = (dataDAO as ConnectionDAO).initiatorDataId;
						xmlData.connections.connection[connectionCount].@end = (dataDAO as ConnectionDAO).targetDataId;
						xmlData.connections.connection[connectionCount].@label = dataDAO.text;
						connectionCount++;
						break;
					case TYPE_DECISION:
						xmlData.decisions.decision[decisionCount] = dataDAO.message;
						xmlData.decisions.decision[decisionCount].@id = dataDAO.dataId;
						xmlData.decisions.decision[decisionCount].@["function"] = (dataDAO as DecisionDAO).decisionFunction;
						xmlData.decisions.decision[decisionCount].@label = dataDAO.text;
						decisionCount++;
						break;
				}				
			}
			trace(xmlData.toXMLString());
			/* 
			var request:URLRequest = new URLRequest("flex.php");
			request.data = xmlData;
			request.contentType = "text/xml";
			request.method = URLRequestMethod.POST;
			var loader:URLLoader = new URLLoader();
			loader.addEventListener( Event.COMPLETE, transferHandler);
			loader.load( request );
			 */
		}
		
		private function transferHandler(evt:Event):void
		{
			/* try
			{
				var success:XML = new XML( evt.target.data );
				
				if( success.toString() == "1" )
				{
					trace("Saved successfully");
				}
				else
				{
					trace("Error encoutered while saving");
				}
			}
			catch(e:TypeError)
			{
				trace("Could not parse XML response from server");
			} */
		}
	}
}